diff options
Diffstat (limited to 'src2/pages/shop/product/[slug].js')
| -rw-r--r-- | src2/pages/shop/product/[slug].js | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/src2/pages/shop/product/[slug].js b/src2/pages/shop/product/[slug].js new file mode 100644 index 00000000..61692c1c --- /dev/null +++ b/src2/pages/shop/product/[slug].js @@ -0,0 +1,305 @@ +import Link from "@/components/elements/Link" +import { useRouter } from "next/router" +import { useEffect, useState } from "react" +import Header from "@/components/layouts/Header" +import apiOdoo from "@/core/utils/apiOdoo" +import { createSlug, getIdFromSlug } from "@/core/utils/slug" +import currencyFormat from "@/core/utils/currencyFormat" +import Layout from "@/components/layouts/Layout" +import { createOrUpdateItemCart } from "@/core/utils/cart" +import toast from "react-hot-toast" +import Footer from "@/components/layouts/Footer" +import Image from "@/components/elements/Image" +import LineDivider from "@/components/elements/LineDivider" +import { HeartIcon as HeartIconSolid } from "@heroicons/react/24/solid" +import { useAuth } from "@/core/utils/auth" +import { HeartIcon } from "@heroicons/react/24/outline" +import LazyLoad from "react-lazy-load" +import ProductSimilar from "@/components/products/ProductSimilar" + +export async function getServerSideProps( context ) { + const { slug } = context.query + let product = await apiOdoo('GET', '/api/v1/product/' + getIdFromSlug(slug)) + if (product?.length == 1) { + product = product[0] + product.description = product.description.replaceAll('<p>', '||p||') + product.description = product.description.replaceAll('</p>', '||/p||') + product.description = product.description.replace(/(<([^>]+)>)/gi, ' ') + product.description = product.description.replaceAll('||p||', '<p>') + product.description = product.description.replaceAll('||/p||', '</p>') + product.description = product.description.trim() + } + return { props: { product } } +} + +export default function ProductDetail({ product }) { + const [ auth ] = useAuth() + const router = useRouter() + const { slug } = router.query + const [selectedVariant, setSelectedVariant] = useState("") + const [quantity, setQuantity] = useState("1") + const [activeVariant, setActiveVariant] = useState({ + id: product.id, + code: product.code, + price: product.lowest_price, + stock: product.stock_total, + weight: product.weight, + attributes: '', + }) + + const [ isAddedToWishlist, setAddedToWishlist ] = useState(false) + const [ activeTab, setActiveTab ] = useState('specification') + + const addOrDeleteWishlist = async () => { + if (auth) { + await apiOdoo('POST', `/api/v1/user/${auth.id}/wishlist/create-or-delete`, { + product_id: product.id + }) + if (isAddedToWishlist) { + toast.success('Berhasil menghapus dari wishlist') + } else { + toast.success('Berhasil menambahkan ke wishlist') + } + setAddedToWishlist(!isAddedToWishlist) + } else { + toast.error('Login terlebih dahulu untuk melanjutkan') + router.push('/login') + } + } + + useEffect(() => { + if (auth) { + const checkWishlist = async () => { + const wishlist = await apiOdoo('GET', `/api/v1/user/${auth.id}/wishlist?product_id=${product.id}`) + setAddedToWishlist(wishlist.product_total > 0 ? true : false) + } + checkWishlist() + } + }, [ auth, product ]) + + useEffect(() => { + if (product.variants.length == 1) { + setSelectedVariant(product.variants[0].id) + } + }, [ product ]) + + useEffect(() => { + if (selectedVariant != '') { + let newActiveVariant = product.variants.filter((variant) => { + return variant.id == selectedVariant + }) + + if (newActiveVariant.length == 1) { + newActiveVariant = newActiveVariant[0] + setActiveVariant({ + id: newActiveVariant.id, + code: newActiveVariant.code, + price: newActiveVariant.price, + stock: newActiveVariant.stock, + weight: newActiveVariant.weight, + attributes: newActiveVariant.attributes.join(', '), + }) + } + } + }, [selectedVariant, product]) + + const onchangeVariant = (e) => { + setSelectedVariant(e.target.value) + } + + const onChangeQuantity = (e) => { + let inputValue = e.target.value + inputValue = parseInt(inputValue) + inputValue = Math.floor(inputValue) + setQuantity(inputValue) + } + + const addItemToCart = () => { + if (product.variant_total > 1 && !selectedVariant) { + toast.error('Pilih varian terlebih dahulu untuk menambahkan ke keranjang', { duration: 2000 }) + return false + } + + if (quantity > 0) { + toast.success('Berhasil menambahkan ke keranjang', { duration: 1500 }) + createOrUpdateItemCart(activeVariant.id, parseInt(quantity)) + } else { + toast.error('Jumlah barang yang ditambahkan minimal 1 pcs', { duration: 2000 }) + } + + return true + } + + const checkoutProduct = () => { + if (!auth) { + toast.error('Login terlebih dahulu untuk melanjutkan', { duration: 2000 }) + router.push('/login') + return + } + if (product.variant_total > 1 && !selectedVariant) { + toast.error('Pilih varian terlebih dahulu untuk melanjutkan pembelian', { duration: 2000 }) + return + } + if (quantity < 0) { + toast.error('Jumlah barang yang ditambahkan minimal 1 pcs', { duration: 2000 }) + return + } + router.push(`/shop/checkout?product_id=${activeVariant.id}&qty=${quantity}`) + } + + const TabButton = ({ children, name }) => ( + <button + type="button" + className={`font-medium pb-1 ${activeTab == name ? 'text-red_r-11 border-b border-red_r-10' : 'text-gray_r-11'}`} + onClick={() => setActiveTab(name)} + > + { children } + </button> + ) + + return ( + <> + <Header title={`${product.name} - Indoteknik`}/> + <Layout> + <Image + src={product.image} + alt={product.name} + className="border-b border-gray_r-6 w-full h-[300px] object-contain object-center bg-white" + /> + + <div className="p-4"> + <div className="flex justify-between gap-x-3"> + <div> + <Link href={'/shop/brands/' + createSlug(product.manufacture.name, product.manufacture.id)}> + {product.manufacture.name ?? '-'} + </Link> + <h1 className="h2 mt-2 mb-3">{product.name}{activeVariant.attributes ? ' - ' + activeVariant.attributes : ''}</h1> + </div> + <button className="h-fit" onClick={addOrDeleteWishlist}> + { isAddedToWishlist && ( + <HeartIconSolid className="w-6 text-red_r-10" /> + ) } + { !isAddedToWishlist && ( + <HeartIcon className="w-6" /> + ) } + </button> + </div> + + {product.variant_total > 1 && !selectedVariant && product.lowest_price.price > 0 ? ( + <p className="text-caption-2 text-gray-800 mb-1">Harga mulai dari:</p> + ) : ''} + + {product.lowest_price.discount_percentage > 0 ? ( + <div className="flex gap-x-1 items-center mb-1"> + <p className="text-caption-2 text-gray_r-11 line-through">{currencyFormat(activeVariant.price.price)}</p> + <span className="badge-solid-red">{activeVariant.price.discount_percentage}%</span> + </div> + ) : ''} + + {product.lowest_price.price > 0 ? ( + <p className="text-body-lg font-semibold">{currencyFormat(activeVariant.price.price_discount)}</p> + ) : ( + <p className="text-gray_r-11">Dapatkan harga terbaik, <a href="">hubungi kami.</a></p> + )} + </div> + + <LineDivider /> + + <div className="p-4"> + <div className=""> + <label className="form-label mb-2">Pilih: <span className="text-gray_r-11 font-normal">{product.variant_total} Varian</span></label> + <select name="variant" className="form-input" value={selectedVariant} onChange={onchangeVariant} > + <option value="" disabled={selectedVariant != "" ? true : false}>Pilih Varian...</option> + {product.variants.length > 1 ? ( + product.variants.map((variant) => { + return ( + <option key={variant.id} value={variant.id}>{variant.attributes.join(', ')}</option> + ) + }) + ) : ( + <option key={product.variants[0].id} value={product.variants[0].id}>{product.variants[0].name}</option> + )} + </select> + </div> + + <label htmlFor="quantity" className="form-label mb-1 mt-3">Jumlah</label> + <div className="flex gap-x-2 mt-2"> + <input type="number" name="quantity" id="quantity" className="form-input h-full w-5/12 text-center" value={quantity} onChange={onChangeQuantity} /> + + <button + className="btn-yellow w-full" + onClick={addItemToCart} + disabled={(product.lowest_price.price == 0 ? true : false)} + > + Keranjang + </button> + <button + onClick={checkoutProduct} + className="btn-solid-red w-full" + > + Beli + </button> + </div> + </div> + + <LineDivider /> + + <div className="p-4"> + <h2 className="font-bold mb-4">Informasi Produk</h2> + <div className="flex gap-x-3 mb-4"> + <TabButton name="specification">Spesifikasi</TabButton> + <TabButton name="description">Deskripsi</TabButton> + <TabButton name="information">Info Penting</TabButton> + </div> + + <div className={`border border-gray_r-6 rounded divide-y ${activeTab == 'specification' ? 'block' : 'hidden'}`}> + <ProductSpecification label="Jumlah Varian"> + <p className="text-gray-800">{product.variant_total} Varian</p> + </ProductSpecification> + <ProductSpecification label="Nomor SKU"> + <p className="text-gray-800" id="sku_number">SKU-{activeVariant.id}</p> + </ProductSpecification> + <ProductSpecification label="Part Number"> + <p className="text-gray-800" id="part_number">{activeVariant.code}</p> + </ProductSpecification> + <ProductSpecification label="Stok"> + <div className="flex gap-x-2" id="stock"> + {activeVariant.stock > 0 ? (activeVariant.stock > 5 && ( + <> + <div className="badge-solid-red">Ready Stock</div> + <div className="badge-gray">{activeVariant.stock > 5 ? '> 5' : '< 5'}</div> + </> + )) : '0'} + </div> + </ProductSpecification> + <ProductSpecification label="Part Number"> + <p className="text-gray-800" id="weight">{activeVariant.weight > 0 ? activeVariant.weight : '1'} KG</p> + </ProductSpecification> + </div> + + <div + className={`text-gray-800 leading-7 ${activeTab == 'description' ? 'block' : 'hidden'}`} + dangerouslySetInnerHTML={{__html: (product.description != '' ? product.description : 'Belum ada deskripsi produk.')}} + ></div> + </div> + + <LineDivider /> + + <LazyLoad> + <ProductSimilar productId={getIdFromSlug(slug || '')} /> + </LazyLoad> + + <Footer /> + </Layout> + </> + ) +} + +const ProductSpecification = ({ children, ...props }) => { + return ( + <div className="flex p-3 justify-between items-center gap-x-1"> + <h3 className="text-gray-900">{ props.label }</h3> + { children } + </div> + ) +}
\ No newline at end of file |
